package com.apisix.service.controller;

import com.apisix.service.entity.Consumer;
import com.apisix.service.service.ConsumerService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

import java.security.MessageDigest;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
public class TokenControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private ConsumerService consumerService;

    private Consumer testConsumer;
    private String authorization;
    private String timestamp;

    @BeforeEach
    public void setup() throws Exception {
        // 创建测试消费者
        testConsumer = new Consumer();
        testConsumer.setId(1L);
        testConsumer.setAppId("AAAAAkey");
        testConsumer.setAppSecret("BBBBBSecret");
        testConsumer.setAppName("APISIX测试应用");
        testConsumer.setDescription("测试消费者");
        testConsumer.setRsaPublicKey("-----BEGIN PUBLIC KEY-----\n" +
                "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo\n" +
                "4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u\n" +
                "+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh\n" +
                "kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ\n" +
                "0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg\n" +
                "cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc\n" +
                "mwIDAQAB\n" +
                "-----END PUBLIC KEY-----");
        testConsumer.setRsaPrivateKey("-----BEGIN PRIVATE KEY-----\n" +
                "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj\n" +
                "MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu\n" +
                "NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ\n" +
                "qgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg\n" +
                "p2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR\n" +
                "ZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi\n" +
                "VuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV\n" +
                "laAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8\n" +
                "sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H\n" +
                "mQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY\n" +
                "dgfGjackLu/+9SiJYVIIIGYzQSOnSCNa8RwTQToUJOhiJGf6fcpdnQNUEaUrpFJl\n" +
                "YZd1oKTtmJybiRHb+3ooYzvFiWlpql/Ios4scCDnZRECgYEA8+YujPVKT5sKVW+Z\n" +
                "oYrHV4PqVV0vqiQqVH3Lh4gwXBgef1Xy2WnA+MgxZFaxrxjnv3RsB9IdKnNl9Og3\n" +
                "XhGeNDUGEL7d9O1QvmJrr+isy+pVRZSBrMOUV+lLjjNXcjFbEUlPp5j+V0DdkcH2\n" +
                "M8L2cJ0l9B4o/vdfM8GW0Yr1dW8CgYEAxMbPqDUGAtWgQrpU4F32cwWLX3LEkWEF\n" +
                "9OBwB/UPyUxdIY0jZJbCkXXQGcD6VXhBqwLQdTeyVQtscjVHcI1qUQjLdwJ7gkYM\n" +
                "3cikGiNjct1OIj2Gn04Z5qMpKwcN4mwvMnIfjQKZnTB84or0PHOMACiQVc0NCwfF\n" +
                "AK2Cw7kzJHUCgYEA3S0NChs7Hv1tjbGDgHcXPH9wTi7dNLxvQGfcYGkAW1qfWgPM\n" +
                "2FZDtKBLv0X2CnTDuGvKFQjGo0U9Dy3w8jmWMVJAQHQo2kZF6V/xzGWMk9aTOsP4\n" +
                "9CIlJWZi7jMPQKEjLdC8gEfGMmjzA+lA1VBBR4W1PgPM+fjXpeRJM4XH+4cCgYEA\n" +
                "w2YDhMFQHLZTZC7kxQd87INgEX6TSjxLzT0Gzw5Gfm9Mr1ZNKgRvHpM2hgJ/JVbi\n" +
                "L8U1m1Qmz7rTi84PbtU+sSLfQ5noIwWl/Sd5Kg4hUGZ9INt7dR4hLAUWzQISSxXx\n" +
                "JnK5LYpYhZ7PMfzIzpLmYB7tRwpXYwIrbkC/+YxpU/kCgYAJ3UbEMbKdvIuK+rES\n" +
                "F9VdlJmJ2g4qXOHkyWrCdOKBBP0xLYKFtymwagYkKRzZGUuCcTwLkaQ2rNMfZHkt\n" +
                "yv5P/xVeKRLKqwu5Wso6OsRoUQ2bY/Z4p4JGqhzjfMZcXOJkMfJJ0X3UAzWpLmpV\n" +
                "4C65qMEL5hR/rAGYjy3EIN0sVg==\n" +
                "-----END PRIVATE KEY-----");

        // 生成当前时间戳
        timestamp = String.valueOf(System.currentTimeMillis());
        
        // 计算授权签名（app_secret+时间戳的MD5哈希值）
        authorization = getMD5Hash(testConsumer.getAppSecret() + timestamp);

        // 设置Mock行为
        when(consumerService.getConsumerByAppId("AAAAAkey")).thenReturn(Optional.of(testConsumer));
    }

    @Test
    public void testGetToken() throws Exception {
        MvcResult result = mockMvc.perform(get("/api/token")
                        .contentType(MediaType.APPLICATION_JSON)
                        .header("Authorization", authorization)
                        .header("Accesskey", "AAAAAkey")
                        .param("t", timestamp))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.token").exists())
                .andExpect(jsonPath("$.expires_in").value(3600))
                .andReturn();

        String content = result.getResponse().getContentAsString();
        System.out.println("响应内容: " + content);
        
        // 验证返回的token不为空
        assertNotNull(content);
        assertTrue(content.contains("token"));
    }

    @Test
    public void testGetTokenWithInvalidAccesskey() throws Exception {
        mockMvc.perform(get("/api/token")
                        .contentType(MediaType.APPLICATION_JSON)
                        .header("Authorization", authorization)
                        .header("Accesskey", "InvalidKey")
                        .param("t", timestamp))
                .andExpect(status().isUnauthorized())
                .andExpect(jsonPath("$.error").value("无效的AccessKey"));
    }

    @Test
    public void testGetTokenWithInvalidAuthorization() throws Exception {
        mockMvc.perform(get("/api/token")
                        .contentType(MediaType.APPLICATION_JSON)
                        .header("Authorization", "InvalidAuth")
                        .header("Accesskey", "AAAAAkey")
                        .param("t", timestamp))
                .andExpect(status().isUnauthorized())
                .andExpect(jsonPath("$.error").value("授权签名无效"));
    }
    
    @Test
    public void testGetTokenWithExpiredTimestamp() throws Exception {
        // 生成一个过期的时间戳（10分钟前）
        String expiredTimestamp = String.valueOf(System.currentTimeMillis() - 10 * 60 * 1000);
        String expiredAuth = getMD5Hash(testConsumer.getAppSecret() + expiredTimestamp);
        
        mockMvc.perform(get("/api/token")
                        .contentType(MediaType.APPLICATION_JSON)
                        .header("Authorization", expiredAuth)
                        .header("Accesskey", "AAAAAkey")
                        .param("t", expiredTimestamp))
                .andExpect(status().isUnauthorized())
                .andExpect(jsonPath("$.error").value("时间戳已过期或无效"));
    }
    
    @Test
    public void testGetTokenWithInvalidTimestampFormat() throws Exception {
        mockMvc.perform(get("/api/token")
                        .contentType(MediaType.APPLICATION_JSON)
                        .header("Authorization", authorization)
                        .header("Accesskey", "AAAAAkey")
                        .param("t", "invalid-timestamp"))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.error").value("无效的时间戳格式"));
    }

    /**
     * 计算字符串的MD5哈希值（大写）
     */
    private String getMD5Hash(String input) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] hashBytes = md.digest(input.getBytes("UTF-8"));
        
        StringBuilder sb = new StringBuilder();
        for (byte b : hashBytes) {
            sb.append(String.format("%02X", b));
        }
        
        return sb.toString();
    }
} 